/**
 * Copyright (C)  Gadglet .
 *
 * This file is part of Gadglet
 *
 * Gadglet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Gadglet is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Gadglet. If not, see <http://www.gnu.org/licenses/>.
 */

package com.gadglet.client.gwt.core;

import java.util.ArrayList;

import com.gadglet.client.gwt.GadgetConfig;
import com.gadglet.client.gwt.GadgetNativeUtils;
import com.gadglet.client.gwt.GadgetUtils;
import com.gadglet.client.gwt.ui.DebugDialogBox;
import com.gadglet.params.FilterByBoolOpTypes;
import com.gadglet.params.FilterByParamTypes;
import com.gadglet.params.FilterByRelOpTypes;
import com.gadglet.params.GadgetType;
import com.gadglet.params.GadgetViewState;
import com.gadglet.params.ReqActionTypes;
import com.gadglet.params.ReqParamNames;
import com.gadglet.params.ReqTypes;
import com.gadglet.params.SortTypes;
import com.google.gwt.http.client.URL;

/**
 * Gadglet query enables to build remote query string to the Gadget server
 * The query attempt to build parameters that will be use on Google App Engine 
 * JDO with as little modification as possible.
 *
 */
public class GadgletQuery {

	private final ArrayList<FilterByItem> filterBy = new ArrayList<FilterByItem>();
	final protected DebugDialogBox errorNotifier = DebugDialogBox.getErrorNotifier();
	private final ArrayList<FilterByParamDeclerationItem> filterByParamDecleration = new ArrayList<FilterByParamDeclerationItem>();

	private final StringBuffer sortBy = new StringBuffer();
	private String returnFields = null;
	private ReqActionTypes requestActionType = ReqActionTypes.VIEW;
	private final ArrayList<String> paramList = new ArrayList<String>();
	private ReqTypes requestType ;
	private String customtActoinName = null;
	private String customtContentName = null;
	private String itemkey = null;
	private int startFrom = -1;
	private int itemNum = -1;
	private GadgetViewState viewType = null;

	
	
	public GadgletQuery(ReqTypes requestType) {
		this.requestType = requestType;
	}
	
	public GadgletQuery() {
		
	}
	
	
	/**
	 * @return content name 
	 */
	String getCustomtContentName() {
		return customtContentName;
	}


	
	public void setCustomtContentName(String customtContentName) {
		this.customtContentName = customtContentName;
	}
	

	
	public GadgetViewState getViewType() {
		return viewType;
	}

	public void setViewType(GadgetViewState viewType) {
		this.viewType = viewType;
	}

	public void setRequestActionType(ReqTypes requestType) {
		this.requestType = requestType;
	}

	

	public void addParam(String paramName, String paramValue) {
		if (!ReqParamNames.isSysParamName(paramName))
			paramList.add(paramName + "=" + URL.encodeComponent(paramValue));
		else
			errorNotifier.showError(55, "you have tried to use a reserved parameter name: "+paramName);
	}
	
	
	public void addParam(ReqParamNames reqParamNames, String paramValue) {
		
			paramList.add(reqParamNames.getParamName() + "=" + URL.encodeComponent(paramValue));
	}
	

	public void changeParamValue(String paramName, String paramValue) {

		deleteParam(paramName);

		addParam(paramName, paramValue);

	}

	public boolean deleteParam(String paramName) {
		boolean delete = false;
		int index = 0;

		for (String param : paramList) {
			if (param.startsWith(paramName + "=")) {
				paramList.remove(index);
				delete = true;
				break;
			}
			index++;
		}

		return delete;
	}

	public ReqTypes getRequestType() {
		return requestType;
	}

	public void setRequestType(ReqTypes type) {
		this.requestType = type;
	}

	private void clearSortBy() {
		sortBy.delete(0, sortBy.length());
	}

	public void clearFilterBy() {
		filterBy.clear();
	}

	public void clearParamList() {
		paramList.clear();
	}

	public void setContentActoinName(String contentActoinName) {
		this.customtActoinName = contentActoinName;
		requestActionType = ReqActionTypes.CUSTOM;
	}

	public boolean filterByExists(String fieldName, String paramName) {

		int index = 0;
		for (FilterByItem fitem : filterBy) {
			if (fitem.getFieldName().equals(fieldName)
					&& fitem.getParamName().equals(paramName)) {
				return true;
			}
			index++;
		}
		return false;
	}

	public void removeFilterBy(String fieldName, String paramName) {

		// delete from filterBy
		int index = 0;
		for (FilterByItem fitem : filterBy) {
			if (fitem.getFieldName().equals(fieldName)
					&& fitem.getParamName().equals(paramName)) {
				filterBy.remove(index);
				break;
			}
			index++;
		}

		// delete from fieldsDeclarioan

		index = 0;
		for (FilterByParamDeclerationItem fitem : filterByParamDecleration) {
			if (fitem.getParamName().equals(paramName)) {
				filterByParamDecleration.remove(index);
				break;
			}
			index++;
		}

		// delete param
		deleteParam(paramName);
	}

	public void addFilterBy(FilterByBoolOpTypes boolOpType, String fieldName,
			FilterByRelOpTypes relOpType, String paramName, String paramValue,
			FilterByParamTypes paramType) {

		if (fieldName != null && paramName != null
				&& !ReqParamNames.isSysParamName(paramName)) {

			filterBy.add(new FilterByItem(boolOpType, fieldName, relOpType,
					paramName));

			paramList.add(paramName + "=" + URL.encodeComponent(paramValue));

			filterByParamDecleration.add(new FilterByParamDeclerationItem(
					paramType, paramName));
		}

	}

	public void setReturnField(String fieldsNames) {
		this.returnFields = fieldsNames;
	}

	public void addSortBy(String fieldNamr, SortTypes order) {
		if (sortBy.length() > 0)
			sortBy.append(", ");

		sortBy.append(fieldNamr + " " + order.getSortType());
	}

	public String getRequestURL() {
		StringBuffer requestUrl = new StringBuffer();
		// server url
		requestUrl.append(GadgetUtils.getGadgetserver(true));
		
		
		GadgetConfig gadget = GadgetConfig.getGadgetConfig();
		
		// servlet url
		if (requestType != null)
			requestUrl.append(requestType.getRequestURI());
		else if(gadget.getGadgetType().equals(GadgetType.MINGLET))
			requestUrl.append(ReqTypes.MINGLET.getRequestURI());
		else
			requestUrl.append(ReqTypes.BIZLET_SIGNED.getRequestURI());
		
		// add gadget name
		requestUrl.append(ReqParamNames.GADGET_PARAM_NAME.getParamName() + "="
				+ gadget.getGadgetName());
		// add action type
		if (requestActionType != null)
			requestUrl.append("&"
					+ ReqParamNames.ACTION_PARAM_NAME.getParamName() + "="
					+ requestActionType.getActionType());
		// add content request name
		if (customtActoinName != null)
			requestUrl.append("&"
					+ ReqParamNames.CUSTOM_ACTION_PARAM_NAME.getParamName()
					+ "=" + customtActoinName);
				
		if (customtContentName != null)
			requestUrl.append("&"
					+ ReqParamNames.CUSTOM_CONTENT_PARAM_NAME.getParamName()
		
					+ "=" + customtContentName);
		
		// add filter by
		if (filterBy.size() > 0) {
			requestUrl.append("&"
					+ ReqParamNames.FILTER_BY_PARAM_NAME.getParamName() + "=");

			int index = 0;
			for (FilterByItem fItem : filterBy) {
				if (index > 0)
					requestUrl.append(URL.encodeComponent(" "
							+ fItem.boolOpType.getOperatorType()));

				requestUrl.append(URL.encodeComponent(" "
						+ fItem.getFieldName()));

				requestUrl.append(URL.encodeComponent(" "
						+ fItem.getRelOpType().getOperatorType()));

				requestUrl.append(URL.encodeComponent(" "
						+ fItem.getParamName()));

			}

		}
		if (filterByParamDecleration.size() > 0) {
			requestUrl.append("&"
					+ ReqParamNames.FILTER_BY_FIELDS_PARAM_NAME.getParamName()
					+ "=");
			for (FilterByParamDeclerationItem pitem : filterByParamDecleration) {
				requestUrl.append(URL.encodeComponent(" "
						+ pitem.getParamType().getParameterType() + " "
						+ pitem.getParamName()));
			}
		}
		// add sort by
		if (sortBy.length() > 0)
			requestUrl.append("&"
					+ ReqParamNames.SORT_BY_PARAM_NAME.getParamName() + "="
					+ URL.encodeComponent(sortBy.toString()));

		// add returning fields
		if (returnFields != null)
			requestUrl.append("&"
					+ ReqParamNames.RETURN_FIELDS_PARAM_NAME.getParamName()
					+ "=" + URL.encodeComponent(this.returnFields));

		// key field
		if (this.itemkey != null)
			requestUrl.append("&" + this.itemkey);

		if (this.startFrom >= 0)
			requestUrl.append("&"
					+ ReqParamNames.ITEMS_START_FROM.getParamName() + "="
					+ this.startFrom);

		if (this.itemNum >= 0)
			requestUrl.append("&" + ReqParamNames.ITEMS_NUM.getParamName()
					+ "=" + this.itemNum);

		if (viewType == null){
			try{
			requestUrl.append("&"
					+ ReqParamNames.GADGET_VIEW_STATE.getParamName() + "="
					+ GadgetNativeUtils.getCurrentView());}
			catch(Exception e){
				// when views not supported, set to home.
				requestUrl.append("&"
						+ ReqParamNames.GADGET_VIEW_STATE.getParamName() + "="
						+ GadgetViewState.HOME.getviewType());
			}
		}
		else
			requestUrl.append("&"
					+ ReqParamNames.GADGET_VIEW_STATE.getParamName() + "="
					+ viewType.getviewType());
		// add app parameters
		for (String param : paramList)
			requestUrl.append("&" + param);

		GadgetConfig gc = GadgetConfig.getGadgetConfig();

		if (gc.isDebugMode() && gc.getDebugServerCode() != null)
			requestUrl.append("&"
					+ ReqParamNames.GADGET_DEBUG_CODE.getParamName() + "="
					+ gc.getDebugServerCode());

		return requestUrl.toString();

	}

	public void setStartItemsFrom(int start) {
		this.startFrom = start;

	}

	public void setItemsNum(int num) {
		this.itemNum = num;
	}

	public int getStartFrom() {
		return startFrom;
	}

	public int getItemNum() {
		return itemNum;
	}

	public void setRequestAction(ReqActionTypes type) {
		this.requestActionType = type;

	}
	
	public ReqActionTypes getRequestAction() {
		return this.requestActionType ;

	}
	

	public void setRequestItemkey(String key) {
		this.itemkey = ReqParamNames.ITEM_KEY_PARAM_NAME.getParamName() + "="
				+ key;

	}

	public void setRequestItemkey(String keyName, String key) {
		if (!ReqParamNames.isSysParamName(keyName))
			this.itemkey = keyName + "=" + key;
	}

	protected class FilterByItem {
		private String fieldName;
		private FilterByBoolOpTypes boolOpType;
		private FilterByRelOpTypes relOpType;
		private String paramName;

		public FilterByItem(FilterByBoolOpTypes boolOpType, String fieldName,
				FilterByRelOpTypes relOpType, String paramName) {

			this.fieldName = fieldName;
			this.boolOpType = boolOpType;
			this.relOpType = relOpType;
			this.paramName = paramName;
		}

		public String getFieldName() {
			return fieldName;
		}

		public FilterByBoolOpTypes getBoolOpType() {
			return boolOpType;
		}

		public FilterByRelOpTypes getRelOpType() {
			return relOpType;
		}

		public String getParamName() {
			return paramName;
		}

	}

	protected class FilterByParamDeclerationItem {
		private FilterByParamTypes paramType;
		private String paramName;

		public FilterByParamDeclerationItem(FilterByParamTypes paramType,
				String paramName) {

			this.paramType = paramType;
			this.paramName = paramName;
		}

		public FilterByParamTypes getParamType() {
			return this.paramType;
		}

		public String getParamName() {
			return this.paramName;
		}

	}

}
